/**
 * @license RequireJS i18n 2.0.6 Copyright (c) 2010-2012, The Dojo Foundation All Rights Reserved.
 * Available via the MIT or new BSD license.
 * see: http://github.com/requirejs/i18n for details
 */
/*jslint regexp: true */
/*global require: false, navigator: false, define: false */

/**
 * This plugin handles i18n! prefixed modules. It does the following:
 *
 * 1) A regular module can have a dependency on an i18n bundle, but the regular
 * module does not want to specify what locale to load. So it just specifies
 * the top-level bundle, like 'i18n!nls/colors'.
 *
 * This plugin will load the i18n bundle at nls/colors, see that it is a root/master
 * bundle since it does not have a locale in its name. It will then try to find
 * the best match locale available in that master bundle, then request all the
 * locale pieces for that best match locale. For instance, if the locale is 'en-us',
 * then the plugin will ask for the 'en-us', 'en' and 'root' bundles to be loaded
 * (but only if they are specified on the master bundle).
 *
 * Once all the bundles for the locale pieces load, then it mixes in all those
 * locale pieces into each other, then finally sets the context.defined value
 * for the nls/colors bundle to be that mixed in locale.
 *
 * 2) A regular module specifies a specific locale to load. For instance,
 * i18n!nls/fr-fr/colors. In this case, the plugin needs to load the master bundle
 * first, at nls/colors, then figure out what the best match locale is for fr-fr,
 * since maybe only fr or just root is defined for that locale. Once that best
 * fit is found, all of its locale pieces need to have their bundles loaded.
 *
 * Once all the bundles for the locale pieces load, then it mixes in all those
 * locale pieces into each other, then finally sets the context.defined value
 * for the nls/fr-fr/colors bundle to be that mixed in locale.
 */
(function () {
  'use strict';

  //regexp for reconstructing the master bundle name from parts of the regexp match
  //nlsRegExp.exec('foo/bar/baz/nls/en-ca/foo') gives:
  //['foo/bar/baz/nls/en-ca/foo', 'foo/bar/baz/nls/', '/', '/', 'en-ca', 'foo']
  //nlsRegExp.exec('foo/bar/baz/nls/foo') gives:
  //['foo/bar/baz/nls/foo', 'foo/bar/baz/nls/', '/', '/', 'foo', '']
  //so, if match[5] is blank, it means this is the top bundle definition.
  var nlsRegExp = /(^.*(^|\/)nls(\/|$))([^\/]*)\/?([^\/]*)/;

  //Helper function to avoid repeating code. Lots of arguments in the
  //desire to stay functional and support RequireJS contexts without having
  //to know about the RequireJS contexts.
  function addPart(locale, master, needed, toLoad, prefix, suffix) {
      if (master[locale]) {
          needed.push(locale);
          if (master[locale] === true || master[locale] === 1) {
              toLoad.push(prefix + locale + '/' + suffix);
          }
      }
  }

  function addIfExists(req, locale, toLoad, prefix, suffix) {
      var fullName = prefix + locale + '/' + suffix;
      if (require._fileExists(req.toUrl(fullName + '.js'))) {
          toLoad.push(fullName);
      }
  }

  /**
   * Simple function to mix in properties from source into target,
   * but only if target does not already have a property of the same name.
   * This is not robust in IE for transferring methods that match
   * Object.prototype names, but the uses of mixin here seem unlikely to
   * trigger a problem related to that.
   */
  function mixin(target, source, force) {
      var prop;
      for (prop in source) {
          if (source.hasOwnProperty(prop) && (!target.hasOwnProperty(prop) || force)) {
              target[prop] = source[prop];
          } else if (typeof source[prop] === 'object') {
              if (!target[prop] && source[prop]) {
                  target[prop] = {};
              }
              mixin(target[prop], source[prop], force);
          }
      }
  }

  define(['module'], function (module) {
      var masterConfig = module.config ? module.config() : {};

      return {
          version: '2.0.6',
          /**
           * Called when a dependency needs to be loaded.
           */
          load: function (name, req, onLoad, config) {
              config = config || {};

              if (config.locale) {
                  masterConfig.locale = config.locale;
              }

              var masterName,
                  match = nlsRegExp.exec(name),
                  prefix = match[1],
                  locale = match[4],
                  suffix = match[5],
                  parts = locale.split('-'),
                  toLoad = [],
                  value = {},
                  i, part, current = '';

              //If match[5] is blank, it means this is the top bundle definition,
              //so it does not have to be handled. Locale-specific requests
              //will have a match[4] value but no match[5]
              if (match[5]) {
                  //locale-specific bundle
                  prefix = match[1];
                  masterName = prefix + suffix;
              } else {
                  //Top-level bundle.
                  masterName = name;
                  suffix = match[4];
                  locale = masterConfig.locale;
                  if (!locale) {
                      locale = masterConfig.locale =
                          typeof navigator === 'undefined' ? 'root' :
                          ((navigator.languages && navigator.languages[0]) ||
                           navigator.language ||
                           navigator.userLanguage || 'root').toLowerCase();
                  }
                  parts = locale.split('-');
              }

              if (config.isBuild) {
                  //Check for existence of all locale possible files and
                  //require them if exist.
                  toLoad.push(masterName);
                  addIfExists(req, 'root', toLoad, prefix, suffix);
                  for (i = 0; i < parts.length; i++) {
                      part = parts[i];
                      current += (current ? '-' : '') + part;
                      addIfExists(req, current, toLoad, prefix, suffix);
                  }

                  req(toLoad, function () {
                      onLoad();
                  });
              } else {
                  //First, fetch the master bundle, it knows what locales are available.
                  req([masterName], function (master) {
                      //Figure out the best fit
                      var needed = [],
                          part;

                      //Always allow for root, then do the rest of the locale parts.
                      addPart('root', master, needed, toLoad, prefix, suffix);
                      for (i = 0; i < parts.length; i++) {
                          part = parts[i];
                          current += (current ? '-' : '') + part;
                          addPart(current, master, needed, toLoad, prefix, suffix);
                      }

                      //Load all the parts missing.
                      req(toLoad, function () {
                          var i, partBundle, part;
                          for (i = needed.length - 1; i > -1 && needed[i]; i--) {
                              part = needed[i];
                              partBundle = master[part];
                              if (partBundle === true || partBundle === 1) {
                                  partBundle = req(prefix + part + '/' + suffix);
                              }
                              mixin(value, partBundle);
                          }

                          //All done, notify the loader.
                          onLoad(value);
                      });
                  });
              }
          }
      };
  });
}());